//
// Created by Scave on 2023/11/11.
//

#include "Scene.h"
#include "component/BasicComponents.h"
#include "LogUtil.h"
#include "MeshUtil.h"
#include "component/InternalComponent.h"
#include "system/physics/PhysicsSystem.h"
#include "system/process/UpdateBroadcastSystem.h"
#include "system/render/CanvasRenderSystem.h"
#include "system/render/SkyboxRenderSystem.h"
#include "system/render/SpriteRenderSystem.h"
#include "system/render/StandardRenderSystem.h"
#include "event/SceneEvent.h"

NS_RIDER_BEGIN

    Scene::Scene(): entities_(events_), systems_(entities_, events_) {
        ResetEntityComponentSystems();
    }

    Scene::Scene(const String& name): entities_(events_), systems_(entities_, events_) {
        this->name_ = name;
        ResetEntityComponentSystems();
    }

    Scene::~Scene() {
        for (auto& entity: entity_list_) {
            entity.destroy();
        }
        entity_list_.clear();
    }

    void Scene::SetCamera(Camera camera) {
        root_entity_.assign_from_copy(camera);
        events_.emit<CameraChangeEvent>(*root_entity_.component<Camera>());
    }

    void Scene::SetDebugInfo(DebugInfo debug_info) {
        if (root_entity_.has_component<DebugInfo>()) {
            root_entity_.component<DebugInfo>()->x = debug_info.x;
            root_entity_.component<DebugInfo>()->y = debug_info.y;
            root_entity_.component<DebugInfo>()->color = debug_info.color;
            root_entity_.component<DebugInfo>()->text = debug_info.text;
        } else {
            root_entity_.assign_from_copy(debug_info);
        }
    }

    void Scene::SetSceneConfig(SceneConfig config) {
        ComponentHandle<SceneConfig> exists = root_entity_.component<SceneConfig>();
        exists->ambient_color = config.ambient_color;
    }

    void Scene::AddEntity(Entity& entity) {
        if (entity_list_.size() >= ENTITY_SIZE_MAX) {
            LOGW("Scene can only add %ud entities", ENTITY_SIZE_MAX);
            return;
        }
        entity_list_.push_back(entity);
    }

    void Scene::AddEntityAt(ENTITY_SIZE_TYPE index, Entity& entity) {
        if (index > entity_list_.size() || entity_list_.size() > ENTITY_SIZE_MAX) {
            LOGW("Scene can only add %ud entities", ENTITY_SIZE_MAX);
            return;
        }
        const std::vector<Entity, std::allocator<Entity>>::const_iterator& end = entity_list_.cend();
        ENTITY_SIZE_TYPE i(0);
        for (auto iterator = entity_list_.cbegin(); iterator != end; ++iterator) {
            if (index == i++) {
                entity_list_.insert(iterator, entity);
                break;
            }
        }
    }

    void Scene::RemoveEntity(Entity& entity) {
        const std::vector<Entity, std::allocator<Entity>>::const_iterator& end = entity_list_.cend();
        for (auto iterator = entity_list_.cbegin(); iterator != end; ++iterator) {
            if (&entity == &*iterator) {
                entity_list_.erase(iterator);
                break;
            }
        }
    }

    void Scene::RemoveEntityAt(ENTITY_SIZE_TYPE index) {
        if (index > entity_list_.size() - 1) {
            LOGW("index can not greater than %ud", ENTITY_SIZE_MAX - 1);
            return;
        }
        const std::vector<Entity, std::allocator<Entity>>::const_iterator& end = entity_list_.cend();
        ENTITY_SIZE_TYPE i(0);
        for (auto iterator = entity_list_.cbegin(); iterator != end; ++iterator) {
            if (index == i++) {
                entity_list_.erase(iterator);
                break;
            }
        }
    }

    Entity Scene::GetEntityAt(ENTITY_SIZE_TYPE index) {
        return entity_list_[index];
    }

    Entity Scene::FindEntity(const Name& name) {
        for (auto& entity: entity_list_) {
            if (entity.has_component<EntityIdentifier>() && entity.component<EntityIdentifier>()->name == name) {
                return entity;
            }
        }
        return {};
    }

    Entity Scene::operator[](ENTITY_SIZE_TYPE index) {
        return GetEntityAt(index);
    }

    Entity Scene::operator[](const Name& name) {
        return FindEntity(name);
    }

    ENTITY_SIZE_TYPE Scene::Size() {
        return entity_list_.size();
    }

    String& Scene::GetName() {
        return name_;
    }

    Entity Scene::CreatePrimitiveEntity(PrimitiveType type, const Name& name) {
        Entity entity = entities_.create();
        entity.assign<MeshRenderModel>();
        entity.assign<Transform>();
        Mesh* mesh = entity.component<MeshRenderModel>()->mesh;
        switch (type) {
            case PrimitiveType::Rectangle:
                MeshUtil::BuildRectangle(mesh, 1, 1);
                break;
            case PrimitiveType::Triangle:
                MeshUtil::BuildTriangle(mesh, 1, 1);
                break;
            case PrimitiveType::Box:
                MeshUtil::BuildBox(mesh, 1, 1, 1);
                break;
            case PrimitiveType::Sphere:
                MeshUtil::BuildSphere(mesh, 30, 30, 0.5);
                break;
            case PrimitiveType::SkyBox:
                MeshUtil::BuildSkyBox(mesh, 1, 1);
                break;
            case PrimitiveType::Capsule:
                MeshUtil::BuildCapsule(mesh, 30, 30, 0.5, 0.5);
                break;
            case PrimitiveType::Cylinder:
                MeshUtil::BuildCylinder(mesh,
                                        0.5, 0.4f, 0.5, 30, 30);
                break;
            case PrimitiveType::Torus:
                MeshUtil::BuildTorus(mesh,
                                     0.5, 0.4, 100, 100);
                break;
            case PrimitiveType::TriangularPyramid:
                MeshUtil::BuildTriangularPyramid(mesh, 1, 1, 1);
                break;
        }
        Vec3 min, max;
        MeshUtil::FindMinMaxXYZ(mesh, min, max);
        entity.component<Transform>()->center = {0, 0, 0};
        entity.assign_from_copy(EntityIdentifier{name});
        return entity;
    }

    EntityManager& Scene::GetEntityManager() {
        return entities_;
    }

    void Scene::Update() {
        root_entity_.component<RenderNodes>()->Reset();
        systems_.update_all(timer_.elapsed());
        events_.emit<SceneUpdatedEvent>(this);
    }

    void Scene::OnLoad() {
        events_.emit<SceneLoadEvent>(this);
    }

    void Scene::OnUnLoad() {
        events_.emit<SceneUnLoadEvent>(this);
    }

    void Scene::OnPause() {
        events_.emit<OnPauseEvent>();
    }

    void Scene::OnResume() {
        events_.emit<OnResumeEvent>();
    }

    void Scene::SetEventListener(SMART_PTR<SceneEventListener>& listener) {
        events_.subscribe<SceneLoadEvent>(*listener);
        events_.subscribe<SceneUnLoadEvent>(*listener);
        events_.subscribe<SceneUpdatedEvent>(*listener);
    }

    void Scene::ResetEntityComponentSystems() {
        root_entity_ = entities_.create();
        root_entity_.assign<RenderNodes>();
        root_entity_.assign<SceneConfig>();
        systems_.add<PhysicsSystem>();
        systems_.add<UpdateBroadcastSystem>();
        systems_.add<StandardRenderSystem>();
        systems_.add<CanvasRenderSystem>();
        systems_.add<SpriteRenderSystem>();
        systems_.add<SkyboxRenderSystem>();
        systems_.configure();
    }

NS_RIDER_END
